package com.mccken.practice;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 可重入锁：state 不只是 0和1 ，还可以大于1
 * 
 * @program: mccken-collect
 * @description:
 * @author: mccken
 * @create: 2023-12-11 18:02
 **/
public class MyLock05 implements Lock {

	@Override
	public void lock() {
		sync.acquire(1);
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {

	}

	@Override
	public boolean tryLock() {
		return false;
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		return false;
	}

	@Override
	public void unlock() {
		sync.release(0);
	}

	@Override
	public Condition newCondition() {
		return null;
	}

	private static class Sync extends AbstractQueuedSynchronizer {
		public boolean tryRelease(int releases) {
			if (getState() == 0 || getExclusiveOwnerThread()!= Thread.currentThread()) {
				throw new IllegalMonitorStateException();
			}
			
			boolean realRelease = false;
			int nextState = getState() - releases;
			
			if (nextState == 0) {
				realRelease = true;
				setExclusiveOwnerThread(null);
			}
			return realRelease;
		}
	}
	
	// 公平独占同步器
	private static class FairSync extends Sync {
		
		public boolean tryAcquire(int acquires) {
			Thread thread = Thread.currentThread();
			int currentState = getState();
			if (currentState == 0) { // 可以获取锁
				// 先判断等待队列中是否有线程在排队，没有线程排队则直接获取锁
				if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
					setExclusiveOwnerThread(thread);
					return true;
				} 
			}
			// 不能获取锁时候，先判断一下当前占有锁的线程，是否同一个线程，如果是，则真加state的值，否则就没法获取锁了
			if (getExclusiveOwnerThread() == thread) {
				// 执行重入逻辑
				int nextState = currentState + acquires;
				if (nextState < 0) {
					throw new Error("Maximum lock count exceeded");
				}
				setState(nextState);
				return true;
			}
			
			return false;
			
		}
	}
	
	private static class NonfairSync extends Sync {
		
		public boolean tryAcquire(int acquires) {
			Thread thread = Thread.currentThread();
			if (getState() == 0) {
				if (compareAndSetState(0, acquires)) {
					setExclusiveOwnerThread(thread);
					return true;
				}
			} else if (getExclusiveOwnerThread() == thread) {
				int nextState = getState() + acquires;
				if (nextState < 0) {
					throw new Error("Maximum lock count exceeded");
				}
				setState(nextState);
				return true;
			}
			return false;
		}
	}
	
	private Sync sync;
	
	private MyLock05(boolean fair) {
		sync = fair? new FairSync() : new NonfairSync();
	}
}
